package com.example.demo.service.Impl;

import com.alibaba.fastjson.JSON;
import com.example.demo.entity.CSDNArticleEntity;
import com.example.demo.entity.CSDNAuthorEntity;
import com.example.demo.service.CSDNSpiderService;
import com.example.demo.utils.CSDNSpiderUtils;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class CSDNSpiderServiceImpl implements CSDNSpiderService {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    // 指定一个主题，两个TAG，分别对应文章消息和作者消息
    private final String TOPIC = "TOPIC_CSDN";
    private final String TAG1 = "ARTICLE";
    private final String TAG2 = "AUTHOR";

    private final String start_url = "https://blog.csdn.net/";
    // 定义一个线程池
    private ThreadPoolExecutor pool = new ThreadPoolExecutor(
            1,
            1,
            10,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2048)
    );

    @Override
    public void startSpider() {
        new Thread(()->{
            // 首先从首页拿到初始的文章url
            Request request = new Request.Builder()
                    .url(start_url)
                    .get()   //默认就是GET请求，可以不写
                    .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36")
                    .build();
            try {
                Response response = CSDNSpiderUtils.client.newCall(request).execute();
                Document dom = Jsoup.parse(response.body().string());
                // 找到文章
                Elements as = dom.getElementsByClass("blog");
                for(Element e : as) {
                    String url = e.attr("href");
                    if(url!=null && !url.equals("")) {
                        CSDNSpiderUtils.queue.offer(url);
                        log.debug("成功添加url：{}", url);
                    }
                }
                // 之后就是不断从队列中拿到url，然后解析顺便拿到更多的url
                while(true) {
                    // 如果队列为空就持续等待
                    while(CSDNSpiderUtils.queue.isEmpty()){}
                    String url = CSDNSpiderUtils.queue.poll();
                    pool.execute(new ArticleParser(url));
                    log.debug("成功消费url：{}", url);
                    Thread.sleep(500);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }


    // 解析每个页面的代码
    private class ArticleParser extends Thread {
        private String url = null;
        public ArticleParser(String url) {
            this.url = url;
        }

        @Override
        public void run() {
            super.run();
            // 判断一下url是否为空
            if(url==null || url.equals("")) {
                return;
            }
            // 先判断一下url是否是文章的url
            if(!url.contains("article")) {
                return;
            }
            // 然后再加载html页面然后解析
            // 建立连接
            Request request = new Request.Builder()
                    .url(url)
                    .get()   //默认就是GET请求，可以不写
                    .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36")
                    .build();
            try {
                Response response = CSDNSpiderUtils.client.newCall(request).execute();
                Document dom = Jsoup.parse(response.body().string());
                // 获取作者信息
                CSDNAuthorEntity authorEntity = assembleAuthorEntity(dom, url);
                if(authorEntity == null) {
                    return;
                }
                // 获取文章信息
                CSDNArticleEntity articleEntity = assembleArticleEntity(dom, url, authorEntity.getAuthor_name(), authorEntity.getNick_name(), authorEntity.getAuthor_score());
                if(!CSDNSpiderUtils.scoreFilter(articleEntity, authorEntity)){
                    return;
                }

                /*
                * 向消息队列中推送消息
                * */
                if(articleEntity!=null) {
                    // 写入到消息队列
                    rocketMQTemplate.send(TOPIC + ":" + TAG1, MessageBuilder.withPayload(articleEntity).build());
                    log.info("获取文章信息：{}", JSON.toJSONString(articleEntity));
                }
                if(authorEntity != null) {
                    // 写入到消息队列
                    rocketMQTemplate.send(TOPIC + ":" + TAG2, MessageBuilder.withPayload(authorEntity).build());
                    log.info("获取作者信息：{}", JSON.toJSONString(authorEntity));
                }

                // 找到文章底部推荐的文章url
                Elements elementsByClass = dom.getElementsByClass("recommend-item-box type_blog clearfix");
                for(Element el : elementsByClass) {
                    String data_url = el.attr("data-url");
                    if(data_url!=null && !data_url.equals("")) {
                        CSDNSpiderUtils.queue.offer(data_url);
                        log.debug("成功添加url：{}", data_url);
                    }
                }
                elementsByClass = dom.getElementsByClass("recommend-item-box baiduSearch clearfix");
                for(Element el : elementsByClass) {
                    String data_url = el.attr("data-url");
                    if(data_url!=null && !data_url.equals("")) {
                        CSDNSpiderUtils.queue.offer(data_url);
                        log.debug("成功添加url：{}", data_url);
                    }
                }
                return;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }
    }

    // 获取文章信息
    private CSDNArticleEntity assembleArticleEntity(Document dom, String url, String username, String nickname, double score) {
        // 找到文章标题
        String title = dom.getElementsByClass("title-article").text();
        // 如果title过长就截取
        if(title.length() >= 150) {
            title = title.substring(0, 150);
        }
        // 找到发表时间
        String up_time = "";
        if(dom.getElementsByClass("time").text().split(" ").length>=2) {
            up_time = dom.getElementsByClass("time").text().split(" ")[1];
        }
        else {
            return null;
        }
        // 找到文章阅读量
        if(dom.getElementsByClass("read-count").text()==null || dom.getElementsByClass("read-count").text().equals("")){
            return null;
        }
        Integer read_volum = Integer.parseInt(dom.getElementsByClass("read-count").text());
        // 找到文章收藏量
        if(dom.getElementsByClass("get-collection").text()==null || dom.getElementsByClass("get-collection").text().equals("")){
            return null;
        }
        Integer collection_volum = Integer.parseInt(dom.getElementsByClass("get-collection").text().split(" ")[0]);

        // 查找文章标签
        StringBuilder sb = new StringBuilder();
        Element element = dom.getElementsByClass("tags-box artic-tag-box").first();
        // 找到所有的a标签
        // 第一个标签是文章分类的，所以忽略掉
        // 第二个是个span，内容是“文章标签”，也忽略掉
        Elements tag_as = element.getElementsByTag("a").next().next();
        for(Element tag_a : tag_as) {
            sb.append(tag_a.text());
            sb.append(" ");
        }
        String str_tag = sb.toString();
        CSDNArticleEntity articleEntity = new CSDNArticleEntity();
        articleEntity.setTitle(title);
        articleEntity.setUp_time(up_time);
        articleEntity.setAuthor_name(username);
        articleEntity.setNick_name(nickname);
        articleEntity.setRead_volum(read_volum);
        articleEntity.setCollection_volum(collection_volum);
        articleEntity.setTag(str_tag);
        articleEntity.setUrl(url);
        articleEntity.setScore(CSDNSpiderUtils.getScore(read_volum, collection_volum, up_time, score));
        return articleEntity;
    }

    // 获取作者信息
    private CSDNAuthorEntity assembleAuthorEntity(Document dom, String url) {
        CSDNAuthorEntity authorEntity = new CSDNAuthorEntity();
        // 从url中获取作者username
        String username = url.split("/article")[0].split("https://blog.csdn.net/")[1];
        authorEntity.setAuthor_name(username);
        // 获取作者的昵称
        Element name_el = dom.getElementsByClass("profile-intro-name-boxTop").first();
        String nick_name = name_el.getElementsByTag("a").first().getElementsByTag("span").text();
        authorEntity.setNick_name(nick_name);
        Element element = dom.getElementsByClass("data-info d-flex item-tiling").get(1);
        Elements elements = element.getElementsByTag("dl");
        try {
            Integer num_fans = Integer.parseInt(elements.get(1).attr("title"));
            Integer num_like = Integer.parseInt(elements.get(2).attr("title"));
            Integer num_comment = Integer.parseInt(elements.get(3).attr("title"));
            Integer num_collect = Integer.parseInt(elements.get(4).attr("title"));
            authorEntity.setNum_fans(num_fans);
            authorEntity.setNum_like(num_like);
            authorEntity.setNum_comment(num_comment);
            authorEntity.setNum_collect(num_collect);
            authorEntity.setAuthor_score(CSDNSpiderUtils.getAuthorScore(num_fans, num_like, num_comment, num_collect));
        } catch (Exception e) {
            log.debug(elements.get(1).attr("title"));
            log.debug("出错的url：{}", url);
        }
        return authorEntity;
    }

}
